package jp.co.jspnet.yahata.osgi.eventhandler.mail;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;

public class EventHandlerImpl implements EventHandler {

    public static final String PROP_FILE_NAME = "eventhandler_mail.properties";
    public static final File PROP_FILE = new File(PROP_FILE_NAME);
    public static final String PROP_KEY_SMTP_HOST =
            "jp.co.jspnet.yahata.osgi.eventhandler.mail.smtp.host";
    public static final String PROP_KEY_SMTP_PORT =
            "jp.co.jspnet.yahata.osgi.eventhandler.mail.smtp.port";
    public static final String PROP_KEY_SMTP_AUTH =
            "jp.co.jspnet.yahata.osgi.eventhandler.mail.smtp.auth";
    public static final String PROP_KEY_SMTP_STARTTLS_ENABLE =
            "jp.co.jspnet.yahata.osgi.eventhandler.mail.smtp.starttls.enable";
    public static final String PROP_KEY_SMTP_USER =
            "jp.co.jspnet.yahata.osgi.eventhandler.mail.smtp.user";
    public static final String PROP_KEY_SMTP_PASSWORD =
            "jp.co.jspnet.yahata.osgi.eventhandler.mail.smtp.password";
    public static final String PROP_KEY_FROM_MAIL_ADDRESS =
            "jp.co.jspnet.yahata.osgi.eventhandler.mail.from.address";
    public static final String PROP_KEY_FROM_NAME =
            "jp.co.jspnet.yahata.osgi.eventhandler.mail.from.name";
    public static final String PROP_KEY_TO_MAIL_ADDRESS =
            "jp.co.jspnet.yahata.osgi.eventhandler.mail.to.address";

    void checkStatus() throws Exception {
        // 設定ファイルの存在をチェックする。
        // 設定ファイルがない場合はfalseを返す。
        if (PROP_FILE.exists() != true) {
            throw new IllegalStateException(
                    "Could not get mail setting file. file path = " + PROP_FILE.getAbsolutePath());
        }
    }

    Dictionary getServiceProperties() {
        String[] topics = {
                EventConstants.EVENT_TOPIC, "org/osgi/framework/BundleEvent/*"};
        Hashtable serviceProperties = new Hashtable();
        serviceProperties.put(EventConstants.EVENT_TOPIC, topics);
        return serviceProperties;
    }

    public void handleEvent(final Event event) {
        if (event == null) {
            // 何もしない。
            return;
        }

        final Object originalEvent = event.getProperty(EventConstants.EVENT);
        if (originalEvent == null
                || (originalEvent instanceof BundleEvent) != true) {
            // 何もしない。
            return;
        }
        final BundleEvent bundleEvent = (BundleEvent) originalEvent;

        // 本当はThreadプールを使いたい。
        Thread notifyThread = new Thread("NOTITY THREAD(Email)") {
            public void run() {
                try {
                    notifyBundleEventByEmail(bundleEvent);
                } catch (Throwable th) {
                    th.printStackTrace();
                }
            }
        };
        notifyThread.start();
    }

    private void notifyBundleEventByEmail(BundleEvent bundleEvent) {
        Bundle eventSourceBundle = bundleEvent.getBundle();
        final String symbolicName = eventSourceBundle.getSymbolicName();
        final int bundleEventType = bundleEvent.getType();
        System.out.println(bundleEventTypeToString(bundleEventType) + ":" + symbolicName);

        // INSTALLED, STARTED, STOPPED, UPDATED, UNINSTALLEDの時だけbodyが入る。
        // これら以外のイベントの場合はnull
        final String mailBody = createMailBody(symbolicName, bundleEventType);

        if (mailBody == null || mailBody.trim().length() == 0) {
            return;
        }

        Properties mailSettingProperties = getMailSettingProperties();

        if (mailSettingProperties == null) {
            System.err.println("Could not get mail setting.");
            return;
        }

        final String smtpHost = mailSettingProperties.getProperty(PROP_KEY_SMTP_HOST);
        final String smtpPort = mailSettingProperties.getProperty(PROP_KEY_SMTP_PORT);
        final String smtpUser = mailSettingProperties.getProperty(PROP_KEY_SMTP_USER);
        final String smtpPassword = mailSettingProperties.getProperty(PROP_KEY_SMTP_PASSWORD);
        final String smtpAuth = mailSettingProperties.getProperty(PROP_KEY_SMTP_AUTH);
        final String smtpStartTLS_enable = mailSettingProperties.getProperty(PROP_KEY_SMTP_STARTTLS_ENABLE);
        final String fromMailAddress = mailSettingProperties.getProperty(PROP_KEY_FROM_MAIL_ADDRESS);
        final String fromName = mailSettingProperties.getProperty(PROP_KEY_FROM_NAME);
        final String toMailAddress = mailSettingProperties.getProperty(PROP_KEY_TO_MAIL_ADDRESS);

        Properties props = System.getProperties();
        // SMTPサーバーのアドレスを指定
        props.put("mail.smtp.host", smtpHost);
        props.put("mail.smtp.port", smtpPort);
        props.put("mail.smtp.auth", smtpAuth);
        props.put("mail.smtp.starttls.enable", smtpStartTLS_enable);

        Session session = Session.getDefaultInstance(props, null);
        MimeMessage mimeMessage = new MimeMessage(session);
        Transport transport = null;
        try {
            // 送信元メールアドレスと送信者名を指定
            mimeMessage.setFrom(new InternetAddress(fromMailAddress, fromName, "iso-2022-jp"));
            // 送信先メールアドレスを指定
            mimeMessage.setRecipients(Message.RecipientType.TO, toMailAddress);
            // メールのタイトルを指定
            mimeMessage.setSubject("バンドルが制御されました。", "iso-2022-jp");
            // メールの内容を指定
            mimeMessage.setContent(mailBody, "text/plain; charset=iso-2022-jp");
            // メールの形式を指定
            mimeMessage.setHeader("Content-Transfer-Encoding", "7bit");
            // 送信日付を指定
            mimeMessage.setSentDate(new Date());
            // 送信します
            transport = session.getTransport("smtp");
            transport.connect(smtpUser, smtpPassword);
            transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
        } catch (MessagingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } finally {
            if (transport != null) {
                try {
                    transport.close();
                } catch (MessagingException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private Properties getMailSettingProperties() {
        Properties properties = new Properties();
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(PROP_FILE);
            properties.load(fis);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        if (properties.isEmpty() == true) {
            return null;
        }

        return properties;
    }

    private String createMailBody(String symbolicName, int bundleEventType) {
        // INSTALLED, STARTED, STOPPED, UPDATED, UNINSTALLEDの時だけ処理する。
        // これら以外のイベントは処理しない。
        final String mailBody;
        switch (bundleEventType) {
        case BundleEvent.INSTALLED:
            mailBody = "Bundle-SymbolicNameが"
                + symbolicName + "のバンドルがインストールされました。";
            break;
        case BundleEvent.STARTED:
            mailBody = "Bundle-SymbolicNameが"
                + symbolicName + "のバンドルがスタートしました。";
            break;
        case BundleEvent.STOPPED:
            mailBody = "Bundle-SymbolicNameが"
                + symbolicName + "のバンドルがストップしました。";
            break;
        case BundleEvent.UPDATED:
            mailBody = "Bundle-SymbolicNameが"
                + symbolicName + "のバンドルがアップデートされました。";
            break;
        case BundleEvent.UNINSTALLED:
            mailBody = "Bundle-SymbolicNameが"
                + symbolicName + "のバンドルがアンインストールされました。";
            break;
        default:
            return null;
        }

        return mailBody;
    }

    private static String bundleEventTypeToString(int bundleEventType) {
        switch (bundleEventType) {
        case BundleEvent.INSTALLED:
            return "INSTALLED";
        case BundleEvent.STARTED:
            return "STARTED";
        case BundleEvent.STOPPED:
            return "STOPPED";
        case BundleEvent.UPDATED:
            return "UPDATED";
        case BundleEvent.UNINSTALLED:
            return "UNINSTALLED";
        case BundleEvent.RESOLVED:
            return "RESOLVED";
        case BundleEvent.UNRESOLVED:
            return "UNRESOLVED";
        default:
            return "UNKNOWN_TYPE(" + bundleEventType + ")";
        }
    }
}
